/*************************************************************************
 *                                                                       *
 *  Copyright 2010, 2011 Yu Yichao                                       *
 *  yyc1992@gmail.com                                                    *
 *                                                                       *
 *  This file is part of asystem.                                        *
 *                                                                       *
 *  asystem is free software: you can redistribute it and/or modify      *
 *  it under the terms of the GNU General Public License as published by *
 *  the Free Software Foundation, either version 3 of the License, or    *
 *  (at your option) any later version.                                  *
 *                                                                       *
 *  asystem is distributed in the hope that it will be useful,           *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 *  GNU General Public License for more details.                         *
 *                                                                       *
 *  You should have received a copy of the GNU General Public License    *
 *  along with asystem.  If not, see <http://www.gnu.org/licenses/>.     *
 *                                                                       *
 *************************************************************************/

#include "asystem.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <wait.h>

using namespace std;

namespace asys
{
#define PIPE_BUF_SIZE 1024
  pid_t asystem( string command , int* read , int* write , int* readerr )
  {
    pid_t pid;
    int fd[3][2];
    if ( pipe(fd[0]) == -1 )//read
      {
	return -1;
      }
    if ( pipe(fd[1]) == -1 )//write
      {
	close(fd[0][0]);
	close(fd[0][1]);
	return -1;
      }
    if ( pipe(fd[2]) == -1 )//readerr
      {
	close(fd[0][0]);
	close(fd[0][1]);
	close(fd[1][0]);
	close(fd[1][1]);
	return -1;
      }
    if ( read != NULL )
      *read = fd[0][0];
    if ( write != NULL )
      *write = fd[1][1];
    if ( readerr != NULL )
      *readerr = fd[2][0];
    if (( pid = fork() ) == -1 )
      {
	close(fd[0][0]);
	close(fd[0][1]);
	close(fd[1][0]);
	close(fd[1][1]);
	close(fd[2][0]);
	close(fd[2][1]);
	return -1;
      }
    if ( pid ) //father
      {
	close(fd[0][1]);
	close(fd[1][0]);
	close(fd[2][1]);
	return pid;
      }
    else //child
      {
	close(fd[0][0]);
	close(fd[1][1]);
	close(fd[2][0]);
	dup2( fd[1][0] , STDIN_FILENO );
	dup2( fd[0][1] , STDOUT_FILENO );
	dup2( fd[2][1] , STDERR_FILENO );
	exit( system( &(command[0]) ) );
      }
  };
  
  int asystem( string command , int mod ,... )
  {
    if ( mod > 7 )
      return -1;
    int* pipefd[3];
    if ( mod & SYSTEM_READ )
      pipefd[0] = new int();
    else
      pipefd[0] = NULL;
    if ( mod & SYSTEM_WRITE )
      pipefd[1] = new int();
    else
      pipefd[1] = NULL;
    if ( mod & SYSTEM_READERR )
      pipefd[2] = new int();
    else
      pipefd[2] = NULL;
    int result;
    int pid = asystem( command , pipefd[0] , pipefd[1] , pipefd[2] );
    if ( pid == -1 )
      {
	for ( int i = 0 ; i < 3 ; i++ )
	  delete pipefd[i];
	return -1;
      }
    string* strlist[3];
    string null[3];
    for ( int i = 0 ; i < 3 ; i++ )
      null[i] = "";
    va_list ap;
    va_start( ap , mod );
    if ( mod & SYSTEM_READ )
      strlist[0] = va_arg( ap , string* );
    else
      strlist[0] = null;
    strlist[1] = null + 1;
    if ( mod & SYSTEM_WRITE )
      *(strlist[1]) = *(va_arg( ap , string* ));
    if ( mod & SYSTEM_READERR )
      strlist[2] = va_arg( ap , string* );
    else
      strlist[2] = null + 2;
    va_end(ap);
    if ( pipefd[1] != NULL )
      {
	write( *pipefd[1] , &((*(strlist[1]))[0]) , strlist[1] -> length() );
	close( *pipefd[1] );
	delete pipefd[1];
      }
    char * buff = new char[PIPE_BUF_SIZE + 1];
    buff[PIPE_BUF_SIZE] = 0;
    waitpid( pid , &result , 0 );
    int size;
    if ( pipefd[0] != NULL )
      {
	while ((size=read( *pipefd[0] , buff , PIPE_BUF_SIZE ))==PIPE_BUF_SIZE)
	  *(strlist[0]) += buff;
	buff[size] = 0;
	*(strlist[0]) += buff;
	close( *pipefd[0] );
	delete pipefd[0];
      }
    if ( pipefd[2] != NULL )
      {
	while ((size=read( *pipefd[2] , buff , PIPE_BUF_SIZE ))==PIPE_BUF_SIZE)
	  *(strlist[2]) += buff;
	buff[size] = 0;
	*(strlist[2]) += buff;
	close( *pipefd[2] );
	delete pipefd[2];
      }
    delete []buff;
    return result;
  };

  string tr( const string& input , char from , char to )
  {
    string result = "";
    for ( unsigned int i = 0 ; i < input.length() ; i++ )
      {
	switch ( from )
	  {
	  case BLANK:
	    if ( isblank(input[i]) ) goto Change;
	    break;
	  case VBLANK:
	    if ( isvblank(input[i]) ) goto Change;
	    break;
	  case HBLANK:
	    if ( ishblank(input[i]) ) goto Change;
	    break;
	  default:
	    if ( input[i] == from ) goto Change;
	    break;
	  Change:
	    switch ( to )
	      {
	      case BLANK:
		break;
	      default:
		result += to;
		break;
	      };
	    continue;
	  };
	result += input[i];
      }
    return result;
  };
  
  bool ishblank( char c )
  {
    if ( c == ' ' or c == '\t' or c == '\b' ) return true;
    return false;
  };
  
  bool isvblank( char c )
  {
    if ( c == '\n' or c == '\v' or c == '\f' ) return true;
    return false;
  };
  
  bool isblank( char c )
  {
    if ( ishblank(c) or isvblank(c) ) return true;
    return false;
  };
  
  vector<string> cut( string input , char del )
  {
    vector<string> result;
    for ( unsigned int i = 0 ; i < input.length() ;)
      {
	for (; i < input.length() ;i++)
	  {
	    switch ( del )
	      {
	      case BLANK:
		if ( isblank(input[i]) ) continue;
		break;
	      case VBLANK:
		if ( isvblank(input[i]) ) continue;
		break;
	      case HBLANK:
		if ( ishblank(input[i]) ) continue;
		break;
	      default:
		if ( input[i] == del ) continue;
		break;
	      };
	    break;
	  }
	if ( i + 1 >= input.length() ) break;
	result.push_back("");
	for (; i < input.length() ; i++ )
	  {
	    switch ( del )
	      {
	      case BLANK:
		if ( isblank(input[i]) ) break;
		result[ result.size() - 1 ] += input[i];
		continue;
	      case VBLANK:
		if ( isvblank(input[i]) ) break;
		result[ result.size() - 1 ] += input[i];
		continue;
	      case HBLANK:
		if ( ishblank(input[i]) ) break;
		result[ result.size() - 1 ] += input[i];
		continue;
	      default:
		if ( input[i] == del ) break;
		result[ result.size() - 1 ] += input[i];
		continue;
	      };
	    break;
	  }
      }
    return result;
  };
  
  int find( const string& str , char d )
  {
    for ( unsigned int i = 0 ; i < str.length() ; i++ )
      if ( str[i] == d ) return i;
    return -1;
  };
  
  int findre( const string& str , char d )
  {
    for ( int i = str.length() - 1 ; i >= 0 ; i++ )
      if ( str[i] == d ) return i;
    return -1;
  };
  
  int find( const vector<string>& v , const string& t )
  {
    for ( unsigned int i = 0 ; i < v.size() ; i++ )
      if ( v[i] == t )
	return i;
    return -1;
  };

  int find( const string& v , const string& t )
  {
    for ( unsigned int i = 0 ; i <= v.length() - t.length() ; i++ )
      if ( v.substr( i , t.length() ) == t )
	return i;
    return -1;
  };
  
  string pwd()
  {
    char *tmp = get_current_dir_name();
    string result( tmp );
    free( tmp );
    return result;
  };
  
  int related_path( const string& v , const string& t )
  {
    string current = pwd();
    if ( cd( v ) != 0 )
      {
	cd( current );
	return -2;
      }
    string vdir = pwd();
    cd( current );
    if ( cd( t ) != 0 )
      {
	cd( current );
	return -2;
      }
    string tdir = pwd();
    cd( current );
    if ( vdir == tdir ) return 0;
    if ( find( vdir , tdir ) == 0 ) return 1;
    if ( find( tdir , vdir ) == 0 ) return 2;
    return -1;
  };

  string getpath( const string& full )
  {
    int loc;
    if (( loc = findre( full , '/' )) < 0 ) return ".";
    return full.substr( 0 , loc ) + '/';
  };
  
  vector<string> getpath( const vector<string>& fulls )
  {
    vector<string> result;
    for ( unsigned int i = 0 ; i < fulls.size() ; i++ )
      {
	bool exist = false;
	string tmp = getpath( fulls[i] );
	for ( unsigned int j = 0 ; j < result.size() ; j++ )
	  if ( tmp == result[j] )
	    {
	      exist = true;
	      break;
	    }
	if ( !exist )
	  result.push_back( tmp );
      }
    return result;
  };
  
  string getname( const string& full )
  {
    int loc;
    if (( loc = findre( full , '/' )) < 0 ) return full;
    return full.substr( loc + 1 , full.length() - loc - 1 );
  };
  
  vector<string> getname( const vector<string>& fulls )
  {
    vector<string> result;
    for ( unsigned int i = 0 ; i < fulls.size() ; i++ )
      {
	bool exist = false;
	string tmp = getname( fulls[i] );
	for ( unsigned int j = 0 ; j < result.size() ; j++ )
	  if ( tmp == result[j] )
	    {
	      exist = true;
	      break;
	    }
	if ( !exist )
	  result.push_back( tmp );
      }
    return result;
  };
  
  
  int in( const string& fullname , const string& path )
  {
    string pathof = getpath( fullname );
    switch ( related_path( pathof , path ))
      {
      case -2:
	return -1;
      case -1:
      case 2:
	return 0;
      case 0:
      case 1:
	return 1;
      };
    return -1;
  };
}
